Đường Dẫn Riêng Cho Từng Ngôn Ngữ
Từ 8.2.0, mỗi ngôn ngữ có thể mang một đường dẫn (slug) riêng:
/gioi-thieu → trang Giới thiệu, tiếng Việt
/en/about-us → cùng trang đó, tiếng Anh
Trước 8.2.0 một đối tượng chỉ có một slug dùng chung, nên bản tiếng Anh buộc phải nằm ở
/en/gioi-thieu.
Tính năng này tắt sẵn trên site nâng cấp: cập nhật lên 8.2.0 không đổi một đường dẫn nào. Bật ở Cấu hình hệ thống → Đường dẫn (tab chỉ hiện khi site có từ hai ngôn ngữ trở lên).
1. Dữ liệu nằm ở đâu
| Bảng | Cột | Vai trò |
|---|---|---|
routes | language | Nguồn sự thật để phân giải URL. NULL = slug dùng chung mọi ngôn ngữ (toàn bộ dữ liệu trước 8.2.0); 'en' = slug chỉ dành cho tiếng Anh |
language | slug | Bản sao để dựng link. joinLanguage() trả cột này ra thành slug cho theme |
system | option language_slug_per_language | Công tắc tính năng |
Một đối tượng có thể có đồng thời dòng NULL (dữ liệu cũ) và dòng theo ngôn ngữ. Lần lưu đầu
tiên sau khi bật cờ, dòng NULL được đổi thành ngôn ngữ mặc định — giữ nguyên id và slug
nên URL cũ không đổi.
2. Bốn quy tắc phân giải
SkillDo\Cms\Routing\SlugResolver nhận slug S và ngôn ngữ đang xem L:
| # | Tình huống | Kết quả |
|---|---|---|
| 1 | Có dòng language = L | Phục vụ |
| 2a | Có dòng NULL, và đối tượng đó đã có dòng L với slug khác | 301 sang slug của L |
| 2b | Có dòng NULL, ch ưa có bản dịch | Phục vụ — URL cũ sống mãi |
| 3a | Chỉ có dòng của ngôn ngữ khác, nhưng đối tượng đã có dòng L | 301 sang slug của L |
| 3b | Chỉ có dòng của ngôn ngữ khác, chưa dịch | Theo config('language.slug.foreign'): render (mặc định) hoặc 404 |
| 4 | Không có dòng nào | 404 |
Quy tắc 2a/3a là thứ giữ SEO: URL cũ không bao giờ chết, chỉ chuyển hướng khi đã thật sự có URL mới để trỏ tới.
Bộ phân giải không hỏi công tắc. Dòng theo ngôn ngữ đã nằm trong DB thì luôn được phục vụ, kể cả khi cờ đã tắt lại — tắt chỉ ngừng tạo thêm.
3. Nhập đường dẫn trong quản trị
Cờ tắt: một ô "Đường dẫn" trong nhóm SEO, y như trước.
Cờ bật: mỗi tab ngôn ngữ có ô "Đường dẫn" riêng, field tên {locale}[slug]. Bỏ trống ô nào thì
ngôn ngữ đó không có slug riêng và URL của nó dùng slug mặc định. Bỏ trống ô slug nhưng có
tiêu đề thì hệ thống tự sinh slug từ tiêu đề của chính ngôn ngữ đó.
Module tự viết không được tự khai ->text('slug', ...) nữa mà gọi:
// Đặt ô slug vào đúng chỗ theo cấu hình hiện tại
$form->slugField();
$form->right()
->addGroup('seo', trans('form.group.seo'))
->text('seo_title', ['label' => 'Meta title']);
Gọi slugField() trước chuỗi seo_title để khi tính năng tắt, ô slug vẫn đứng đầu nhóm SEO
đúng như bố cục cũ. Nhóm SEO nằm trong một FormAdminLocation riêng thì truyền vào:
$form->slugField(['label' => 'Slug'], ['location' => $formSeo, 'groupName' => trans('form.group.seo')]);
slugField() tự bỏ qua việc tách ô nếu model không dùng cả ModelRoute lẫn ModelLanguage
— thiếu một trong hai thì slug người dùng gõ vào sẽ bị nuốt mất.
4. Dựng URL
Chỉ có một câu hỏi cần trả lời trước khi chọn hàm:
Link này dành cho ngôn ngữ đang xem, hay cho một ngôn ngữ khác?
| Bạn đang làm gì | Dùng |
|---|---|
| Link bài viết / sản phẩm / danh mục ngay trong trang | Url::permalink($object->slug) — giữ nguyên như cũ |
| Bộ chuyển ngôn ngữ | Url::language($lang) |
Thẻ hreflang, sitemap, canonical đa ngữ | Url::localized($object) |
| Cần slug thô theo từng ngôn ngữ để tự ghép URL | Url::localizedSlugs($object) |
4.1 Url::permalink() vẫn đúng — và không phải sửa gì
Đây là điều quan trọng nhất của cả trang tài liệu này: hơn 120 chỗ trong theme và plugin đang
gọi Url::permalink($object->slug) đều tiếp tục chạy đúng, không cần đụng tới.
// đang xem tiếng Việt
Url::permalink($page->slug); // vi/gioi-thieu
// đang xem tiếng Anh — CÙNG dòng code đó
Url::permalink($page->slug); // en/about-us
Hai mắt xích làm việc đó:
joinLanguage()khiến$object->slugđã là slug của ngôn ngữ đang xem (xem mục 1).- Filter
get_url(plugin đa ngôn ngữ) thêm tiền tố ngôn ngữ, y như trước 8.2.0.